home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / MacHaskell 2.2 / progs / demo / X11 / logo / logo.hs < prev    next >
Encoding:
Text File  |  1994-09-27  |  44.7 KB  |  1,340 lines  |  [TEXT/YHS2]

  1. {-
  2.  
  3. Ki-Wing Ho and Eric Fox
  4. Computer Science 429b
  5. Professor Hudak
  6. Final Project:  LOGO Interpreter
  7.  
  8. -}
  9.  
  10.  
  11.  
  12. -------------------------------------------------------------------------------
  13. module REPLoop where
  14.  
  15. {-
  16.  
  17. REPLoop has two main parts: the first part (function logo) sets up the
  18. graphics window, prints a welcome message, initializes the variable
  19. and procedure environments and the turtle, accepts and lines's the
  20. user input, runs the read-eval-print loop (part two), and then closes
  21. the graphics window and exists; the second part (function repLoop)
  22. lexes and parses each command, prints an error message if there was a
  23. syntax error and evaluates (or tries to) if there wasn't, and then
  24. either prints the value or an error message or exits if the value
  25. returnd by the evaluator is "GoodBye".
  26.  
  27. -}
  28.  
  29. import Lexer
  30. import Parser
  31. import Evaluator
  32. import Xlib
  33.  
  34. demo = main
  35.  
  36. main = getEnv "DISPLAY" >>= \ host ->
  37.        logo host
  38.  
  39. logo :: String -> IO ()
  40.  
  41. logo host =
  42.   xOpenDisplay host >>= \ display ->
  43.  
  44.   let (screen:_) = xDisplayRoots display
  45.       fg_color = xScreenWhitePixel screen
  46.       bg_color = xScreenBlackPixel screen
  47.       root = xScreenRoot screen
  48.   in
  49.   xCreateWindow root
  50.                 (XRect 100 100 500 500)
  51.                 [XWinBackground bg_color,
  52.                  XWinBackingStore XAlwaysBackStore] 
  53.   >>= \ graphWindow ->
  54.   xSetWmName graphWindow "Logo" >>
  55.   xSetWmIconName graphWindow "Logo" >>
  56.   xMapWindow graphWindow >>
  57.  
  58.   xCreateGcontext (XDrawWindow root)
  59.                   [XGCBackground bg_color,
  60.                    XGCForeground fg_color] >>= \ graphContext ->
  61.  
  62.   xDisplayForceOutput display >>
  63.  
  64.   putStr ("Welcome to LOGO!\n" ++ prompt) >>
  65.   getContents stdin >>= \userInput ->
  66.   repLoop 
  67.     (varEnvsInit,procEnvsInit,turtleInit)
  68.     ((lines userInput,Lexer),
  69.      (graphWindow,display,graphContext,bg_color,fg_color)) $
  70.   xCloseDisplay display
  71.  
  72. -- Initial Environments --
  73.  
  74. varEnvsInit :: VarsType
  75. varEnvsInit  = [[("GOODBYE",GoodBye)]]
  76.  
  77. -- all user-defined commands must have dummy entries
  78. procEnvsInit :: ProcsType
  79. procEnvsInit = (map (makeFakeProc)
  80.                     [("XCOR",0),("YCOR",0),("GETANGLE",0),("GETPEN",0),
  81.                      ("GETTURTLE",0),
  82.                      ("SUM",2),("DIFFERENCE",2),("PRODUCT",2),("MOD",2),
  83.                      ("DIV",2),("POWER",2),
  84.                      ("AND",2),("OR",2),("NOT",1),
  85.                      ("WORDP",1),("LISTP",1),("NUMBERP",1),("GREATER",2),
  86.                      ("EQUAL",2),("LESS",2),
  87.                      ("BUTFIRST",1),("FPUT",2),("CONCAT",2),
  88.                      ("FIRST",1),("LAST",1),("WORD",-2),("LIST",-2),
  89.                      ("SENTENCE",-2), ("USE",1)]):[]
  90.  
  91. turtleInit :: TurtleType
  92. turtleInit   = (500 `div` 2,500 `div` 2,90,True,False)
  93.  
  94. -- makes a dummy procedure
  95. makeFakeProc :: (NameType , Int) -> (NameType , ProcType)
  96. makeFakeProc (name,num) = (name,(makeArgs num,[]))
  97.  
  98. makeArgs :: Int -> [NameType]
  99. makeArgs n | n > 0     = "" : makeArgs (n-1)
  100.            | otherwise = []
  101.  
  102. -- keep running Read-Eval-Print Loop until user types GoodBye
  103. -- repLoop keeps running until user types "GoodBye", alternately
  104. --   lexing, parsing, and evaluating each command
  105. -- after a syntax error, the lex state is reset
  106. repLoop :: EnvsType -> StateType -> IO () -> IO ()
  107. repLoop e1 (inS1,gs1) end = 
  108.   let fail1 msg (is1,ls1) = errorOutput msg >>
  109.                             repLoop e1 ((is1,Lexer),gs1) end
  110.         -- parser fail continuation doesn't contain graphics state
  111.       fail2 msg ((is2,ls2),gs2) = errorOutput msg >>
  112.                                   repLoop e1 ((is2,Lexer),gs1) end
  113.         -- evaluator fail continuation does contain graphics state
  114.   in
  115.     parse [] inS1 fail1 $ \a ts inS2 ->
  116.     if (null ts)
  117.       then
  118.         evaluate e1 a (inS2,gs1) fail2 $ \v e2 ((is3,ls3),gs3) ->
  119.         output v end $
  120.         repLoop e2 ((is3,Lexer),gs3) end
  121.       else
  122.         fail1 "Syntax error:  expected end of line" inS2
  123.         -- repLoop will still be rerun
  124.  
  125. -- print error message
  126. errorOutput :: String -> IO ()
  127. errorOutput error = putStr (error ++ prompt)
  128.  
  129. -- print expression value, exiting if GoodBye
  130. output :: Value -> IO () -> IO () -> IO ()
  131. output GoodBye end succ 
  132.   = putStr "\nGoodbye!\n" >> end
  133. output v       end succ
  134.   = putStr ((valueToString v) ++ prompt) >> succ
  135.  
  136. prompt :: String
  137. prompt = "\nLOGO> "
  138.  
  139.  
  140.  
  141. -------------------------------------------------------------------------------
  142. module Evaluator where
  143.  
  144. {-
  145.  
  146. Evaluator takes an Abstract Syntax Tree and evaluates it in the
  147. current environment, returning both the resultant value and the new
  148. environment (as well as the updated state, of which only the user
  149. input can actually be changed in the evaluator).
  150.  
  151. A value can be of one of six types:  integer, string, list, and
  152. boolean, as well as null (for commands which don't return anything and
  153. newly-declared local variables), and goodbye, which allows logo to
  154. quit.
  155.  
  156. The environment consists of three parts.  The variable environment and
  157. the procedure environment are separate (so that a name can refer both
  158. to a variable and a procedure:  Logo syntax is such that there is
  159. never any ambiguity) are both lists of name-value association lists.
  160. Each association list representes a "local environment", with each
  161. successive one being more "global", so that the last environment in
  162. the list is the global environment.  Local environments are produced
  163. by user-function invocations and removed at the end of those
  164. invocations.
  165.  
  166. -}
  167.  
  168. import Lexer
  169. import Parser
  170. import Xlib
  171.  
  172. type NameType      = [Char]
  173. type WordType      = [Char]
  174. type Error         = [Char]
  175.  
  176. type StateType     = (InputState , GraphicsState)
  177. type GraphicsState = (XWindow , XDisplay , XGcontext , XPixel , XPixel)
  178. type EnvsType      = (VarsType,ProcsType,TurtleType)
  179. type VarsType      = [[(NameType , Value)]]
  180. type ProcsType     = [[(NameType , ProcType)]]
  181. type TurtleType    = (Int , Int , Int , Bool , Bool)
  182. type ProcType      = ([NameType] , ClauseType)
  183.  
  184. data Value         = Null
  185.                    | Num Int
  186.                    | Word WordType
  187.                    | List ListType
  188.                    | Boolean Bool
  189.                    | GoodBye
  190.                      deriving Text
  191.  
  192. data ListType      = NullList | Value :* ListType
  193.                      deriving Text
  194.  
  195.  
  196. type EvalFailType  = Error -> StateType -> IO ()
  197. type EvalSuccType  = Value -> EnvsType -> StateType -> IO ()
  198. type EvalResType   = StateType -> EvalFailType -> EvalSuccType -> IO ()
  199. type EvaluateType  = EnvsType -> AST -> EvalResType
  200.  
  201.  
  202. evaluate :: EvaluateType
  203.  
  204. evaluate (vs,p:ps,ttl) (To newName newProc)         ss fail succ
  205.   = succ Null (vs,((newName,newProc):p):ps,ttl) ss
  206.   -- procedures
  207.  
  208. evaluate e             (Read)                       ((i:is,ls),gs) fail succ
  209.   = succ (List (makeReadList (lexerReadLine i))) e ((is,ls),gs)
  210.   -- user input
  211.  
  212. evaluate e1            (Print [a])                  ss fail succ
  213.   = evaluate e1 a ss fail $ \v e2 ss2 ->
  214.     putStr ((valueToString v)++"\n") >>
  215.     succ Null e2 ss2
  216.   -- user output
  217.  
  218. evaluate e             (Argument (Val (Word n)))    ss fail succ
  219.   = lookup e n ss fail $ \v ->
  220.     succ v e ss
  221.   -- variable reference
  222.  
  223. evaluate e             (Argument (Val v))           ss fail succ
  224.   = succ v e ss
  225.   -- constant
  226.  
  227. evaluate e             (Argument (QuotedWordArg n)) ss fail succ
  228.   = succ (Word n) e ss
  229.   -- string constant
  230.  
  231. evaluate (v:vs,ps,ttl) (Local n)                    ss fail succ
  232.   = succ Null (((n,Null):v):vs,ps,ttl) ss
  233.   -- local variable declaraion
  234.   -- local returns null, and sets the new local variable to null also
  235.  
  236. evaluate e             (ParseList l)                ss fail succ
  237.   = succ (List l) e ss
  238.   -- lists (also constant)
  239.  
  240. evaluate e             (Loop l cond insts)          ss fail succ
  241.   = evalLoop l e cond insts ss fail succ
  242.   -- loops
  243.  
  244. evaluate e             (If cond thens elses)        ss fail succ
  245.   = evalIf e cond thens elses ss fail succ
  246.   -- if-then[-eles] conditionals
  247.  
  248. evaluate e1            (Command name as1)           ss fail succ
  249.   | ((na == length as1) || (na == -2))
  250.     = evalArgs e1 as1 ss fail $ \e2 as2 ss2 ->
  251.       apply name as2 e2 ss2 fail $ \v e3 ss3 ->
  252.       succ v e3 ss3
  253.   | na == -1
  254.     = fail ("Function does not exist:  " ++ name) ss
  255.   | otherwise
  256.     = fail ("Wrong number of arguments to " ++ name) ss
  257.   where na = numArgs e1 name
  258.   -- function applications
  259.  
  260. evaluate e1            (Make n a)                   ss fail succ
  261.   = evaluate e1 a ss fail $ \v e2 ss2 ->
  262.     update e2 n v $ \e3 ->
  263.     succ v e3 ss2
  264.   -- assignment statements, which return the assigned value
  265.  
  266. evaluate e1            (Graphics name as1)          ss fail succ
  267.   = evalArgs e1 as1 ss fail $ \e2 as2 ss2 ->
  268.     doGraphics name as2 e2 ss2 fail $ \e3 ss3 ->
  269.     succ Null e3 ss3
  270.   -- side-effecting graphics statements, which all return null
  271. -- end evaluate
  272.  
  273.  
  274. -- evaluate a list of actual parameters, returning the corresponding
  275. --   list of values
  276. evalArgs :: EnvsType -> ParseArgs -> StateType -> EvalFailType ->
  277.             (EnvsType -> EvalArgs -> StateType -> IO ()) -> IO ()
  278. evalArgs e  []      ss fail succ
  279.   = succ e [] ss
  280. evalArgs e1 (a:as1) ss fail succ
  281.   = evaluate e1 a ss fail $ \v e2 ss2 ->
  282.     evalArgs e2 as1 ss2 fail $ \e3 as2 ss3 ->
  283.     succ e3 (v:as2) ss3
  284.  
  285.  
  286. -- evaluate a list of commands, returning the value of the last one
  287. evalClause :: EnvsType -> ClauseType -> EvalResType
  288. evalClause e  []     ss fail succ
  289.   = succ Null e ss
  290. evalClause e  (a:[]) ss fail succ
  291.   = evaluate e a ss fail succ
  292. evalClause e1 (a:as) ss fail succ
  293.   = evaluate e1 a ss fail $ \v e2 ss2 ->
  294.     evalClause e2 as ss2 fail succ
  295.  
  296. -- convert a lexed user-input list to a list constant
  297. makeReadList :: [WordType] -> ListType
  298. makeReadList []              = NullList
  299. makeReadList (w:ws) = (Word w) :* (makeReadList ws)
  300.  
  301.  
  302. -- Variable routines --
  303.  
  304. -- look up a variable reference in the variable environment
  305. -- search the most-local environments first
  306. -- return an error if not found
  307. lookup :: EnvsType -> NameType -> StateType -> EvalFailType ->
  308.           (Value -> IO ()) -> IO ()
  309. lookup ([],ps,ttl)             name ss fail succ
  310.   = fail ("Unbound variable:  " ++ name) ss
  311. lookup ([]:vss,ps,ttl)         name ss fail succ
  312.   = lookup (vss,ps,ttl) name ss fail succ
  313. lookup (((n,v):vs):vss,ps,ttl) name ss fail succ
  314.   | n == name = succ v
  315.   | otherwise = lookup (vs:vss,ps,ttl) name ss fail succ
  316.  
  317. -- update the variable environment
  318. -- replace the most-local occurrance  first; if none are found,
  319. --   create a new variable and place it in the most-global environment
  320. update :: EnvsType -> NameType -> Value -> (EnvsType -> IO ()) -> IO ()
  321. update ([]:[],ps,ttl) name value succ
  322.   = succ (((name,value):[]):[],ps,ttl)
  323. update ([]:vss,ps,ttl) name value succ
  324.   = update (vss,ps,ttl) name value $ \(vss2,ps2,ttl2) ->
  325.     succ ([]:vss2,ps2,ttl2)
  326. update (((n,v):vs):vss,ps,ttl) name value succ
  327.   | n == name = succ (((n,value):vs):vss,ps,ttl)
  328.   | otherwise = update (vs:vss,ps,ttl) name value $ \(vs2:vss2,ps2,ttl2) ->
  329.                 succ (((n,v):vs2):vss2,ps2,ttl2)
  330.  
  331.  
  332. -- Control structures --
  333.  
  334. -- evaluate loops
  335. evalLoop :: LoopType -> EnvsType -> ConditionType -> ClauseType ->
  336.             EvalResType
  337. evalLoop Do     = evalDo
  338. evalLoop While  = evalWhile
  339. evalLoop Repeat = evalRepeat
  340.  
  341. -- evaluate while statements
  342. -- loop semantics:  evaluate condition; if true, evaluate clause, then loop
  343. -- while returns null
  344. evalWhile :: EnvsType -> ConditionType -> ClauseType -> EvalResType
  345. evalWhile e1 cond insts ss fail succ
  346.   = evalCond e1 cond ss fail $ \b e2 ss2 ->
  347.     if b
  348.       then
  349.         evalClause e2 insts ss2 fail $ \v e3 ss3 ->
  350.         evalWhile e3 cond insts ss3 fail succ
  351.       else
  352.         succ Null e2 ss2
  353.  
  354. -- evaluate do-while statements
  355. -- loop semantics:  evaluate clause then evaluate condition; if true, loop
  356. evalDo :: EnvsType -> ConditionType -> ClauseType -> EvalResType
  357. evalDo e1 cond insts ss fail succ
  358.   = evalClause e1 insts ss fail $ \v e2 ss2 ->
  359.     evalCond e2 cond ss2 fail $ \b e3 ss3 ->
  360.     if b
  361.       then 
  362.         evalDo e3 cond insts ss3 fail succ
  363.       else
  364.         succ Null e3 ss3
  365.  
  366. -- evaluate repeat statements
  367. -- loop semantics:  evaluate loop number as n; evaluate clause n times
  368. -- evaluate loop number and print error if it is negative or not an integer
  369. evalRepeat :: EnvsType -> ConditionType -> ClauseType -> EvalResType
  370. evalRepeat e1 cond insts ss fail succ
  371.   = evaluate e1 cond ss fail $ \v e2 ss2 ->
  372.     case v of
  373.       Num n     -> if (n >= 0)
  374.                      then doIterations e2 n insts ss2 fail succ
  375.                      else fail "Repeat: Iteration count cannot be negative" ss2
  376.       otherwise -> fail "Repeat:  Invalid iteration count" ss2
  377.  
  378. -- perform loop interations:  evaluate "insts" "n" times
  379. doIterations :: EnvsType -> Int -> ClauseType -> EvalResType
  380. doIterations e  0     insts ss fail succ
  381.   = succ Null e ss
  382. doIterations e1 (n+1) insts ss fail succ
  383.   = evalClause e1 insts ss fail $ \v e2 ss2 ->
  384.     doIterations e2 n insts ss2 fail succ
  385.  
  386. -- evaluates conditions and returns either true, false, or an error
  387. evalCond :: EnvsType -> ConditionType -> StateType -> EvalFailType ->
  388.             (Bool -> EnvsType -> StateType -> IO ()) -> IO ()
  389. evalCond e1 cond ss fail succ 
  390.   = evaluate e1 cond ss fail $ \v e2 ss2 ->
  391.     case v of
  392.       Boolean b -> succ b e2 ss2
  393.       otherwise -> fail "Invalid condition" ss2
  394.  
  395. -- evaluate if-then[-else] statements
  396. evalIf :: EnvsType -> ConditionType -> ClauseType -> ClauseType -> EvalResType
  397. evalIf e1 cond thens elses ss fail succ
  398.   = evalCond e1 cond ss fail $ \b e2 ss2 ->
  399.     if b
  400.       then evalClause e2 thens ss2 fail succ
  401.       else evalClause e2 elses ss2 fail succ
  402.  
  403.  
  404. -- Function application --
  405.  
  406. -- returns the number of arguments to a user-defined or built-in function
  407. -- -1 means the function wasn't found
  408. -- -2 means the function can take any number of arguments
  409. numArgs :: EnvsType -> CommandName -> Int
  410. numArgs (vs,[],ttl)     name
  411.   = -1
  412. numArgs (vs,[]:pss,ttl) name 
  413.   = numArgs (vs,pss,ttl) name
  414. numArgs (vs,((n,(formals,body)):ps):pss,ttl) name
  415.   | inList ["WORD","SENTENCE","LIST"] name = -2
  416.   | n == name                              = length formals
  417.   | otherwise                              = numArgs (vs,ps:pss,ttl) name
  418.  
  419. -- apply a function to its arguments
  420. -- mostly just decides if it's user-defined or built-in, then dispatches
  421. apply :: CommandName -> EvalArgs -> EnvsType -> EvalResType
  422. apply n as e ss fail succ
  423.   | isBuiltIn n = applyPrimProc n as e ss fail succ
  424.   | otherwise   = applyUserProc (getProc e n) as e ss fail succ
  425.  
  426.  
  427.  
  428. -- returns procedure "name" from the procedure environment
  429. -- searches most-local environments first
  430. -- precondition:  procedure does exist somewhere
  431. getProc :: EnvsType -> CommandName -> ProcType
  432. getProc (vss,[]:pss,ttl)        name
  433.   = getProc (vss,pss,ttl) name
  434. getProc (vs,((n,p):ps):pss,ttl) name
  435.   | n == name = p
  436.   | otherwise = getProc (vs,ps:pss,ttl) name
  437.  
  438. -- apply user function:
  439. --   bind formal parameters
  440. --   create local enviroments
  441. --   evaluate body of function
  442. --   destroy local environments
  443. --   return value of body
  444. applyUserProc :: ProcType -> EvalArgs -> EnvsType -> EvalResType
  445. applyUserProc (formals,body) actuals e1 ss fail succ
  446.   = bind formals actuals e1 $ \e2 ->
  447.     evalClause e2 body ss fail $ \v (vs:vss,ps:pss,ts) ss2 ->
  448.     succ v (vss,pss,ts) ss2
  449.  
  450. -- bind formal parameters to actuals in local environment
  451. bind :: [NameType] -> EvalArgs -> EnvsType -> (EnvsType -> IO ()) -> IO ()
  452. bind formals actuals (vss,pss,ttl) succ
  453.   = succ ((zip formals actuals):vss,[]:pss,ttl)
  454.  
  455.  
  456. -- Built-in functions --
  457.  
  458. -- returns true for built-in functions
  459. isBuiltIn :: CommandName -> Bool
  460. isBuiltIn = inList ["XCOR","YCOR","GETANGLE","GETPEN","GETTURTLE",
  461.                     "SUM","DIFFERENCE","PRODUCT","MOD","DIV","POWER",
  462.                     "AND","OR","NOT",
  463.                     "WORDP","LISTP","NUMBERP","GREATER","EQUAL","LESS",
  464.                     "BUTFIRST","FPUT","CONCAT",
  465.                     "FIRST","LAST","WORD","LIST","SENTENCE", "USE"]
  466.  
  467.  
  468. -- applies a built-in function to its arguments
  469. applyPrimProc :: CommandName -> [Value] -> EnvsType -> EvalResType
  470.  
  471. applyPrimProc "XCOR"      [] (vs,ps,(x,y,a,p,t)) ss fail succ
  472.   = succ (Num x) (vs,ps,(x,y,a,p,t)) ss
  473. applyPrimProc "YCOR"      [] (vs,ps,(x,y,a,p,t)) ss fail succ
  474.   = succ (Num y) (vs,ps,(x,y,a,p,t)) ss
  475. applyPrimProc "GETANGLE"  [] (vs,ps,(x,y,a,p,t)) ss fail succ
  476.   = succ (Num a) (vs,ps,(x,y,a,p,t)) ss
  477. applyPrimProc "GETPEN"    [] (vs,ps,(x,y,a,p,t)) ss fail succ
  478.   = succ (Boolean p) (vs,ps,(x,y,a,p,t)) ss
  479. applyPrimProc "GETTURTLE" [] (vs,ps,(x,y,a,p,t)) ss fail succ
  480.   = succ (Boolean t) (vs,ps,(x,y,a,p,t)) ss
  481.  
  482. applyPrimProc "SUM"        [Num a , Num b] e ss fail succ
  483.   = succ (Num (a+b)) e ss
  484. applyPrimProc "DIFFERENCE" [Num a , Num b] e ss fail succ
  485.   = succ (Num (a-b)) e ss
  486. applyPrimProc "PRODUCT"    [Num a , Num b] e ss fail succ
  487.   = succ (Num (a*b)) e ss
  488. applyPrimProc "MOD"        [Num a , Num b] e ss fail succ
  489.   = succ (Num (a `mod` b)) e ss
  490. applyPrimProc "DIV"        [Num a , Num b] e ss fail succ
  491.   = succ (Num (a `div` b)) e ss
  492. applyPrimProc "POWER"      [Num a , Num b] e ss fail succ
  493.   | b >= 0 = succ (Num (a^b)) e ss
  494.   | otherwise = fail ("Negative exponent:  " ++ (show b)) ss
  495.  
  496. applyPrimProc "AND" [Boolean a , Boolean b] e ss fail succ
  497.   = succ (Boolean (a && b)) e ss
  498. applyPrimProc "OR"  [Boolean a , Boolean b] e ss fail succ
  499.   = succ (Boolean (a || b)) e ss
  500. applyPrimProc "NOT" [Boolean a]             e ss fail succ
  501.   = succ (Boolean (not a)) e ss
  502.  
  503. applyPrimProc "WORDP"   [Word w]                e ss fail succ
  504.   = succ (Boolean True) e ss
  505. applyPrimProc "WORDP"   [v]                     e ss fail succ
  506.   = succ (Boolean False) e ss
  507. applyPrimProc "NUMBERP" [Num n]                 e ss fail succ
  508.   = succ (Boolean True) e ss
  509. applyPrimProc "NUMBERP" [v]                     e ss fail succ
  510.   = succ (Boolean False) e ss
  511. applyPrimProc "LISTP"   [List l]                e ss fail succ
  512.   = succ (Boolean True) e ss
  513. applyPrimProc "LISTP"   [v]                     e ss fail succ
  514.   = succ (Boolean False) e ss
  515. applyPrimProc "GREATER" [Num a , Num b]         e ss fail succ
  516.   = succ (Boolean (a > b)) e ss
  517. applyPrimProc "EQUAL"   [Num a , Num b]         e ss fail succ
  518.   = succ (Boolean (a == b)) e ss
  519. applyPrimProc "EQUAL"   [Word a , Word b]       e ss fail succ
  520.   = succ (Boolean (a == b)) e ss
  521. applyPrimProc "EQUAL"   [Boolean a , Boolean b] e ss fail succ
  522.   = succ (Boolean (a == b)) e ss
  523. applyPrimProc "LESS"    [Num a , Num b]         e ss fail succ
  524.   = succ (Boolean (a < b)) e ss
  525.  
  526. applyPrimProc "BUTFIRST" [Word ""]                     e ss fail succ
  527.   = succ (Word "") e ss
  528. applyPrimProc "BUTFIRST" [Word (c:cs)]                 e ss fail succ
  529.   = succ (Word cs) e ss
  530. applyPrimProc "BUTFIRST" [List NullList]               e ss fail succ
  531.   = succ (List NullList) e ss
  532. applyPrimProc "BUTFIRST" [List (v :* vs)]              e ss fail succ
  533.   = succ (List vs) e ss
  534. applyPrimProc "FPUT"     [v , List l]                  e ss fail succ
  535.   = succ (List (v :* l)) e ss
  536. applyPrimProc "CONCAT"   [List l1 , List l2]           e ss fail succ
  537.   = succ (List (listConcatenate l1 l2)) e ss
  538. applyPrimProc "FIRST"    [Word (c:cs)]                 e ss fail succ
  539.   = succ (Word (c:[])) e ss
  540. applyPrimProc "FIRST"    [List (v :* vs)]              e ss fail succ
  541.   = succ v e ss
  542. applyPrimProc "LAST"     [Word (c:[])]                 e ss fail succ
  543.   = succ (Word (c:[])) e ss
  544. applyPrimProc "LAST"     [Word ""]                     e ss fail succ
  545.   = succ Null e ss
  546. applyPrimProc "LAST"     [Word (c:cs)]                 e ss fail succ
  547.   = applyPrimProc "LAST" [(Word cs)] e ss fail succ
  548. applyPrimProc "LAST"     [List (v :* NullList)]        e ss fail succ
  549.   = succ v e ss
  550. applyPrimProc "LAST"     [List (v :* vs)]              e ss fail succ
  551.   = applyPrimProc "LAST" [(List vs)] e ss fail succ
  552. applyPrimProc "WORD"     []                            e ss fail succ
  553.   = succ (Word "") e ss
  554. applyPrimProc "WORD"     ((Word w):ws)                 e ss fail succ
  555.   = applyPrimProc "WORD" ws e ss fail $ \(Word wsc) e2 ss2 ->
  556.     succ (Word (w ++ wsc)) e2 ss2
  557. applyPrimProc "LIST"     (v:vs)                        e ss fail succ
  558.   = applyPrimProc "LIST" vs e ss fail $ \(List l) e2 ss2 ->
  559.     succ (List (v :* l)) e2 ss2
  560. applyPrimProc "LIST"     []                            e ss fail succ
  561.   = succ (List NullList) e ss
  562. applyPrimProc "SENTENCE" []                            e ss fail succ
  563.   = succ (List NullList) e ss
  564. applyPrimProc "SENTENCE" ((List l):[])                 e ss fail succ
  565.   = succ (List l) e ss
  566. applyPrimProc "SENTENCE" ((List l):vs)                 e ss fail succ
  567.   = applyPrimProc "SENTENCE" [List l] e  ss  fail $ \(List s1) e2 ss2 ->
  568.     applyPrimProc "SENTENCE" vs       e2 ss2 fail $ \(List s2) e3 ss3 ->
  569.     succ (List (listConcatenate s1 s2)) e3 ss3
  570. applyPrimProc "SENTENCE" (v:vs)                        e ss fail succ
  571.   = applyPrimProc "SENTENCE" vs e ss fail $ \(List ws) e2 ss2 ->
  572.     succ (List (v :* ws)) e2 ss2
  573.  
  574. applyPrimProc "USE" [Word filename]                   
  575.               e 
  576.               ss@((ins, ls), gs)
  577.               fail succ
  578.   = readFile filename >>=  \filecontents ->
  579.     useRepLoop e ((lines filecontents, Lexer), gs) 
  580.                (\ msg s -> fail msg ss) $ \ v e s -> 
  581.     succ v e ss
  582.                          
  583. applyPrimProc n          _                             _ ss fail _
  584.   = fail ("Incorrect arguments:  " ++ n) ss
  585.  
  586. useRepLoop :: EnvsType -> EvalResType 
  587. useRepLoop e  s@(([], ls), gs) fail succ = succ (Word "OK") e s
  588. useRepLoop e1 s1@(inS1,gs1) fail succ = 
  589.     parse [] inS1 (\ msg ins -> fail msg (ins, gs1)) $ \a ts inS2 ->
  590.     if (null ts)
  591.       then
  592.         evaluate e1 a (inS2,gs1) fail $ \v e2 s3 ->
  593.         useRepLoop e2 s3 fail succ
  594.       else
  595.         fail "Syntax error:  expected end of line" (inS2, gs1)
  596.  
  597.  
  598.  
  599. -- concatenates two lists
  600. listConcatenate :: ListType -> ListType -> ListType
  601. listConcatenate NullList  l2 = l2
  602. listConcatenate (v :* l1) l2 = (v :* (listConcatenate l1 l2))
  603.  
  604.  
  605. -- Graphics --
  606.  
  607. type EvalArgs = [Value]
  608. type GraphEnv = (Int,Int,Int,Bool)
  609.  
  610. -- evaluates side-effecting graphics functions
  611. -- note:  none of them return values
  612. doGraphics :: CommandName -> EvalArgs -> EnvsType -> StateType -> 
  613.               EvalFailType -> (EnvsType -> StateType -> IO ()) -> IO ()
  614.  
  615. doGraphics "HIDETURTLE" [] (vs,ps,(x,y,a,p,t)) ss fail succ
  616.   = hideTurtle x y a ss $
  617.     succ (vs,ps,(x,y,a,p,False)) ss
  618.   -- hide turtle, appropriately adjust environment
  619.  
  620. doGraphics "SHOWTURTLE" [] (vs,ps,(x,y,a,p,t)) ss fail succ
  621.   = showTurtle x y a ss $
  622.     succ (vs,ps,(x,y,a,p,True)) ss
  623.   -- show turtle, appropriately adjust environment
  624.  
  625. doGraphics name as (vs,ps,(x,y,a,p,True)) ss fail succ
  626.   = hideTurtle x y a ss $
  627.     moveTurtle name as (x,y,a,p) ss $ \(x2,y2,a2,p2) ->
  628.     showTurtle x2 y2 a2 ss $
  629.     succ (vs,ps,(x2,y2,a2,p2,True)) ss
  630.   -- executes graphics commands if turtle is shownn
  631.  
  632. doGraphics name as (vs,ps,(x,y,a,p,False)) ss fail succ
  633.   = moveTurtle name as (x,y,a,p) ss $ \(x2,y2,a2,p2) ->
  634.     succ (vs,ps,(x2,y2,a2,p2,False)) ss
  635.   -- executes graphics commands if turtle is not shown
  636.  
  637. -- converts an integer to a float
  638. toFloat :: Int -> Float
  639. toFloat = fromInteger . toInteger
  640.  
  641. newmod a b = let c = a `mod` b
  642.              in if (c < 0) then (c + b) else c
  643.  
  644. -- shows the turtle, but returns nothing
  645. showTurtle :: Int -> Int -> Int -> StateType -> IO () -> IO ()
  646. showTurtle x y a (is,(graphWindow,display,graphContext,bg,fg)) succ
  647.   = let dx1 = round (12 * cos (toFloat a * pi/180))
  648.         dx2 = round (4  * sin (toFloat a * pi/180))
  649.     dy1 = round (12 * sin (toFloat a * pi/180))
  650.     dy2 = round (4  * cos (toFloat a * pi/180))
  651.     in 
  652.     xDrawLine (XDrawWindow graphWindow) 
  653.               graphContext
  654.           (XPoint x y) 
  655.           (XPoint (x-dx1-dx2) (y+dy1-dy2))
  656.     >>
  657.     xDrawLine (XDrawWindow graphWindow)
  658.               graphContext
  659.           (XPoint x y)
  660.           (XPoint (x-dx1+dx2) (y+dy1+dy2))
  661.     >>
  662.     xDrawLine (XDrawWindow graphWindow)
  663.               graphContext
  664.           (XPoint (x-dx1-dx2) (y+dy1-dy2))
  665.           (XPoint (x-dx1+dx2) (y+dy1+dy2))
  666.     >>
  667.     xDisplayForceOutput display
  668.     >>
  669.     succ
  670.  
  671. -- hides the turtle, but returns nothing
  672. hideTurtle :: Int -> Int -> Int -> StateType -> IO () -> IO ()
  673. hideTurtle x y a (is,(graphWindow,display,graphContext,bg,fg)) succ
  674.   = xUpdateGcontext graphContext [XGCForeground bg] 
  675.     >>
  676.     (showTurtle x y a (is,(graphWindow,display,graphContext,bg,fg)) $
  677.     (xUpdateGcontext graphContext [XGCForeground fg]
  678.     >>
  679.     succ))
  680.  
  681. -- performs all graphics commands that don't involve hiding/showing 
  682. --   the turtle
  683. moveTurtle :: CommandName -> EvalArgs -> GraphEnv -> StateType ->
  684.               (GraphEnv -> IO ()) -> IO ()
  685. moveTurtle "SETXY"       [Num xp,Num yp] (x,y,a,p) ss succ
  686.   = succ (xp,yp,a,p)
  687.  
  688. -- move the turtle forward "d" times, drawing a line if pen is down
  689. moveTurtle "FORWARD"     [Num d]         (x,y,a,p) 
  690.            (is,(graphWindow,display,graphContext,fg,bg)) succ
  691.   = let xp = x + round (toFloat d * cos (toFloat a * pi/180))
  692.         yp = y - round (toFloat d * sin (toFloat a * pi/180)) in 
  693.      (if p 
  694.         then (xDrawLine (XDrawWindow graphWindow) 
  695.                     graphContext
  696.             (XPoint x y) 
  697.             (XPoint xp yp))
  698.         else return ()) >>
  699.      xDisplayForceOutput display >>
  700.      succ (xp,yp,a,p)
  701.  
  702. -- move the turtle backward "d" pixels, drawing a line if pen is down
  703. moveTurtle "BACKWARD"    [Num d]         (x,y,a,p) ss succ
  704.   = moveTurtle "FORWARD" [Num (-d)] (x,y,a,p) ss succ
  705.  
  706. -- rotate turtle to "ap" degrees from facing due east
  707. moveTurtle "SETANGLE"    [Num ap]        (x,y,a,p) ss succ
  708.   = succ (x,y,ap,p)
  709.  
  710. -- rotate turtle counterclockwise "ap" degrees
  711. moveTurtle "LEFT"        [Num ap]        (x,y,a,p) ss succ
  712.   = succ (x,y, (a + ap) `newmod` 360 ,p)
  713.  
  714. -- rotate turtle clockwise "ap" degrees
  715. moveTurtle "RIGHT"       [Num ap]        (x,y,a,p) ss succ
  716.   = succ (x,y, (a - ap) `newmod` 360 ,p)
  717.  
  718. -- pick pen up
  719. moveTurtle "PENUP"       []              (x,y,a,p) ss succ
  720.   = succ (x,y,a,False)
  721.  
  722. -- put pen down
  723. moveTurtle "PENDOWN"     []              (x,y,a,p) ss succ
  724.   = succ (x,y,a,True)
  725.  
  726. -- clear screen but don't otherwise alter turtle state
  727. moveTurtle "CLEARSCREEN" []              (x,y,a,p) 
  728.            (is,(graphWindow,display,graphContext,bg,fg)) succ
  729.   = xClearArea graphWindow (XRect 0 0 500 500) True  >>
  730.     xDisplayForceOutput display >>
  731.     succ (x,y,a,p)
  732.  
  733. -- pick pen up and reset turtle
  734. moveTurtle "CLEAN"       []              (x,y,a,p) 
  735.            (is,(graphWindow,display,graphContext,bg,fg)) succ
  736.   = xClearArea graphWindow (XRect 0 0 500 500) True >>
  737.     xDisplayForceOutput display >>
  738.     succ (500 `div` 2,500 `div` 2,90,True)
  739.  
  740. -- do nothing if arguments are incorrect
  741. moveTurtle _ _ e _ succ = succ e
  742.  
  743.  
  744. -- valueToString, etc. --
  745.  
  746. -- convert a value to a string
  747. valueToString :: Value -> String
  748. valueToString (Word w)        = w
  749. valueToString (Num n)         = show n
  750. valueToString (Boolean True)  = "TRUE"
  751. valueToString (Boolean False) = "FALSE"
  752. valueToString Null            = ""
  753. valueToString (List l)        = "[" ++ (listToString l) ++ "]"
  754. valueToString GoodBye         = "Don't play around with this variable!"
  755.  
  756. -- convert a list to a string
  757. listToString :: ListType -> String
  758. listToString NullList        = ""
  759. listToString (v :* NullList) = valueToString v
  760. listToString (v :* l)        = (valueToString v) ++ " " ++ (listToString l)
  761.  
  762.  
  763.  
  764. -------------------------------------------------------------------------------
  765. module Lexer where
  766.  
  767. {-
  768.  
  769. Lexer takes as input a line from standard input and returns an ordered
  770. pair containing the translation of that list into tokens as well as
  771. the current state of the lexer (how many parentheses and brackets are
  772. still open).  The state is necessary because some commands may take
  773. multiple lines, so a bracket (say) may be left open on one line to be
  774. closed later on.
  775.  
  776. All unmatched close brackets and parentheses are treated as spaces
  777. (and therefore ignored).
  778.  
  779. The method for tokenizing commands is:
  780.  
  781.   All words are delimited by spaces, parenthesis, or brackets.
  782.  
  783.   All words beginning with a double quote are returned as quoted words
  784.   rather than normal words.
  785.  
  786.   Any character preceded by a backslash is taken as is, rather than
  787.   tokenized normally.
  788.  
  789.   All words are translated to upper case..
  790.  
  791. The method for tokenizing user input is:
  792.  
  793.   All words are delimited by spaces and translated to upper case.
  794.   
  795. -}
  796.  
  797. import Parser
  798. import Evaluator
  799.  
  800.  
  801. data LexState = Lexer | LexerBracket Int LexState | LexerParen Int LexState
  802.                 deriving Text
  803.  
  804. type LexerType = [Char] -> ([Token] , LexState)
  805.  
  806. data Token   = OpenBracket 
  807.              | CloseBracket 
  808.              | OpenParen 
  809.              | CloseParen
  810.              | QuotedWord WordType
  811.              | Normal WordType     deriving (Text,Eq)
  812.  
  813.  
  814. -- call appropriate lex procedure depending upon the current lex state
  815. lexDispatch :: LexState -> LexerType
  816. lexDispatch (Lexer)            = lexer
  817. lexDispatch (LexerBracket n s) = lexerBracket n s
  818. lexDispatch (LexerParen n s)   = lexerParen n s
  819.  
  820.  
  821. -- handle commands
  822. lexer :: LexerType
  823. lexer []       = ([] , Lexer)
  824. lexer (' ':cs) = lexer cs
  825. lexer ('[':cs) = let (ts , s) = lexerBracket 1 (Lexer) cs
  826.                  in (OpenBracket : ts , s)
  827. lexer ('(':cs) = let (ts , s) = lexerParen 1 (Lexer) cs
  828.                  in (OpenParen : ts , s)
  829. lexer (')':cs) = lexer cs
  830. lexer (']':cs) = lexer cs
  831. lexer ('"':cs) = let (t , cs2) = lexerWord (isDelimiter) cs
  832.                      (ts , s)  = lexer cs2
  833.                  in ((QuotedWord (upWord t)):ts , s)
  834. lexer cs       = let (t , cs2) = lexerWord (isDelimiter) cs
  835.                      (ts , s)  = lexer cs2
  836.                  in ((Normal (upWord t)):ts , s)
  837.  
  838. lexerWord :: (Char -> Bool) -> [Char] -> (WordType , [Char])
  839. lexerWord endCond []
  840.   = ([] , [])
  841. lexerWord endCond (c:cs)
  842.   | c == '\\' = if cs == []
  843.                   then ("\\" , cs)
  844.                   else 
  845.                     let (t , cs2) = lexerWord endCond (tail cs)
  846.                     in ((head cs):t , cs2)
  847.   | endCond c = ([] , (c:cs))
  848.   | otherwise = let (t , cs2) = lexerWord endCond cs
  849.                 in ((toUpper c):t , cs2)
  850.  
  851.  
  852. -- performs lexing inside brackets
  853. lexerBracket :: Int -> LexState -> LexerType
  854. lexerBracket n s []
  855.   = ([] , LexerBracket n s)
  856. lexerBracket n s (' ':cs)
  857.   = lexerBracket n s cs
  858. lexerBracket 1 s (']':cs)
  859.   = let (ts , s2) = lexDispatch s cs
  860.     in (CloseBracket:ts , s2)
  861. lexerBracket n s (']':cs)
  862.   = let (ts , s2) = lexerBracket (n-1) s cs
  863.     in (CloseBracket:ts , s2)
  864. lexerBracket n s ('[':cs)
  865.   = let (ts , s2) = lexerBracket (n+1) s cs
  866.     in (OpenBracket:ts , s2)
  867. lexerBracket n s ('(':cs)
  868.   = let (ts , s2) = lexerParen 1 (LexerBracket n s) cs
  869.     in (OpenParen:ts , s2)
  870. lexerBracket n s (')':cs)
  871.   = lexerBracket n s cs
  872. lexerBracket n s cs
  873.   = let (t , cs2) = lexerWord (isDelimiter) cs
  874.         (ts , s2) = lexerBracket n s cs2
  875.     in ((Normal (upWord t)):ts , s2)
  876.  
  877.  
  878. -- performs lexing inside parentheses
  879. lexerParen :: Int -> LexState -> LexerType
  880. lexerParen n s []
  881.   = ([] , LexerParen n s)
  882. lexerParen n s (' ':cs)
  883.   = lexerParen n s cs
  884. lexerParen 1 s (')':cs)
  885.   = let (ts , s2) = lexDispatch s cs
  886.     in (CloseParen:ts , s2)
  887. lexerParen n s (')':cs)
  888.   = let (ts , s2) = lexerParen (n-1) s cs
  889.     in (CloseParen:ts , s2)
  890. lexerParen n s ('(':cs)
  891.   = let (ts , s2) = lexerParen (n+1) s cs
  892.     in (OpenParen:ts , s2)
  893. lexerParen n s ('[':cs)
  894.   = let (ts , s2) = lexerBracket 1 (LexerParen n s) cs
  895.     in (OpenBracket:ts , s2)
  896. lexerParen n s (']':cs)
  897.   = lexerParen n s cs
  898. lexerParen n s ('"':cs)
  899.   = let (t , cs2) = lexerWord (isDelimiter) cs
  900.         (ts , s2) = lexerParen n s cs2
  901.     in ((QuotedWord (upWord t)):ts , s2)
  902. lexerParen n s cs
  903.   = let (t , cs2) = lexerWord (isDelimiter) cs
  904.         (ts , s2) = lexerParen n s cs2
  905.     in ((Normal (upWord t)):ts , s2)
  906.  
  907.  
  908. -- returns true for delimiters
  909. isDelimiter :: Char -> Bool
  910. isDelimiter = inList " []()"
  911.  
  912. -- returns true of p is in cs
  913. inList :: (Eq a) => [a] -> a -> Bool
  914. inList [] p     = False
  915. inList (c:cs) p = (c == p) || (inList cs p)
  916.  
  917.  
  918. -- handle user input
  919. lexerReadLine :: [Char] -> [WordType]
  920. lexerReadLine []
  921.   = []
  922. lexerReadLine (' ':cs)
  923.   = lexerReadLine cs
  924. lexerReadLine cs
  925.   = let (firstWord,restOfWords) = span (/= ' ') cs 
  926.     in (upWord firstWord) : lexerReadLine restOfWords
  927.  
  928. -- translate a word to upper case
  929. upWord :: WordType -> WordType
  930. upWord = map (toUpper)
  931.  
  932.  
  933.  
  934. -------------------------------------------------------------------------------
  935. module Parser where
  936.  
  937. {-
  938.  
  939. Parser takes a list of tokens, the input state, and fail and success
  940. continuations and returns an Abstract Syntax Tree, the remaining
  941. tokens (hopefully none), and the new input state.  The input state
  942. will be changed every time Parser runs out of tokens:  it simply grabs
  943. (and lexes) the next line of user-input.  It therefore doesn't return
  944. anything until the entire AST has been be read in, even if it spans
  945. several lines, though parse may catch some errors before all lines
  946. have been input.  In this case, it ceases taking input and returns the
  947. error.
  948.  
  949. An Abstract Syntax Tree represents one command, and breaks those
  950. commands into Ifs, Loops, Tos, Locals, Makes, Reads, Prints,
  951. Constants, List constants, Graphics commands (which produce
  952. side-effects), and function applications.  All built-in commands that
  953. don't fit into one of those categories are lumped into function
  954. applications along with user-defined functions.  Each type of AST is
  955. parsed into subcommands, subclauses (lists of commands), command
  956. arguments (also subcommands), and any other values that will be
  957. immediately-evaluatable (such as function names).
  958.  
  959. -}
  960.  
  961.  
  962. import Lexer
  963. import Evaluator
  964.  
  965.  
  966. type CommandName     = [Char]
  967. type ClauseType      = [AST]
  968. type ConditionType   = AST
  969.  
  970. type ParseArgs       = [AST]
  971.  
  972. data ArgType         = Val Value | QuotedWordArg WordType
  973.                        deriving Text
  974.  
  975. data AST             = ParseList ListType
  976.                      | If ConditionType ClauseType ClauseType
  977.                      | Loop LoopType ConditionType ClauseType
  978.                      | To NameType ProcType
  979.                      | Make NameType AST
  980.                      | Local NameType
  981.                      | Read
  982.                      | Print ParseArgs
  983.                      | Argument ArgType
  984.                      | Graphics CommandName ParseArgs
  985.                      | Command CommandName ParseArgs      deriving Text
  986.  
  987. data LoopType        = Do | While | Repeat
  988.                        deriving Text
  989.  
  990. type ParseFailType   = Error -> InputState -> IO ()
  991. type ParseType       = [Token] -> InputState -> ParseFailType ->
  992.                        (AST -> [Token] -> InputState -> IO ()) -> IO ()
  993. type ParseClauseType = [Token] -> InputState -> ParseFailType -> 
  994.                        (ClauseType -> [Token] -> InputState -> IO ()) -> IO ()
  995.  
  996. type InputState      = ([[Char]] , LexState)
  997.  
  998. parse :: ParseType
  999.  
  1000. parse [] (i:is , ls) fail succ 
  1001.   = let (ts , ls2) = lexDispatch ls i
  1002.     in parse ts (is , ls2) fail succ  
  1003.  
  1004. parse ((QuotedWord s) : ts) inS fail succ 
  1005.   = succ (Argument (QuotedWordArg s)) ts inS
  1006.  
  1007. parse ((Normal s) : ts) inS fail succ
  1008.   = succ (Argument (Val (process s))) ts inS
  1009.  
  1010. parse (OpenParen : []) (i:is,ls) fail succ
  1011.   = let (ts,ls2) = lexDispatch ls i
  1012.     in parse (OpenParen:ts) (is,ls2) fail succ
  1013.  
  1014. parse (OpenParen : (Normal t) : ts) inS fail succ
  1015.   | t == "TO"    = makeProc ts inS fail succ
  1016.   | t == "MAKE"  = makeMake ts inS fail succ
  1017.   | t == "LOCAL" = makeLocal ts inS fail succ
  1018.   | t == "READ"  = makeRead ts inS fail succ
  1019.   | t == "PRINT" = makePrint ts inS fail succ
  1020.   | t == "IF"    = makeIf ts inS fail succ
  1021.   | isLoop t     = makeLoop t ts inS fail succ
  1022.   | isGraphics t = makeGraphics t ts inS fail succ
  1023.   | otherwise    = makeCommand t ts inS fail succ
  1024.  
  1025. parse (OpenBracket : ts) inS fail succ
  1026.   = parseList ts inS fail succ
  1027.  
  1028. parse ts inS@([], _) _ succ = succ (Argument (Val (Word "GOODBYE"))) ts inS
  1029.  
  1030. parse _ inS fail _
  1031.   = fail "Syntax error" inS
  1032.  
  1033.  
  1034. -- returns true for all loop names
  1035. isLoop :: CommandName -> Bool
  1036. isLoop = inList ["DO","WHILE","REPEAT"]
  1037.  
  1038. -- returns true for all side-effecting graphics command names
  1039. isGraphics :: CommandName -> Bool
  1040. isGraphics = inList ["FORWARD","BACKWARD","LEFT","RIGHT",
  1041.                      "SETXY","SETANGLE","PENUP","PENDOWN",
  1042.                      "HIDETURTLE","SHOWTURTLE","CLEARSCREEN","CLEAN"]
  1043.  
  1044. -- Parse lists --
  1045.  
  1046. -- parses a list constant
  1047. parseList :: ParseType
  1048. parseList []                  (i:is,ls) fail succ
  1049.   = let (ts,ls2) = lexDispatch ls i
  1050.     in parseList ts (is,ls2) fail succ
  1051. parseList (CloseBracket:ts)   inS fail succ
  1052.   = succ (ParseList NullList) ts inS
  1053. parseList (OpenBracket:ts)    inS fail succ
  1054.   = parseList ts inS fail $ \(ParseList l1) ts2 inS2 ->
  1055.     parseList ts2 inS2 fail $ \(ParseList l2) ts3 inS3 ->
  1056.     succ (ParseList ((List l1) :* l2)) ts3 inS3
  1057. parseList ((Normal w):ts)     inS fail succ
  1058.   = parseList ts inS fail $ \(ParseList l) ts2 inS2 ->
  1059.     succ (ParseList ((process w) :* l)) ts2 inS2
  1060. parseList (OpenParen:ts)      inS fail succ
  1061.   = parseList ts inS fail $ \(ParseList l) ts2 inS2 ->
  1062.     succ (ParseList ((Word "(") :* l)) ts2 inS2
  1063. parseList (CloseParen:ts)     inS fail succ
  1064.   = parseList ts inS fail $ \(ParseList l) ts2 inS2 ->
  1065.     succ (ParseList ((Word ")") :* l)) ts2 inS2
  1066. parseList ((QuotedWord w):ts) inS fail succ
  1067.   = parseList ts inS fail $ \(ParseList l) ts2 inS2 ->
  1068.     succ (ParseList ((Word w) :* l)) ts2 inS2
  1069.  
  1070.  
  1071. -- parses constant values, distinguishing words from integers and booleans
  1072. process :: WordType -> Value
  1073. process "TRUE"  = Boolean True
  1074. process "FALSE" = Boolean False
  1075. process ('-':w)
  1076.   | all isDigit w = Num (- (stringToNum (reverse w)))
  1077.   | otherwise     = Word ('-':w)
  1078. process w
  1079.   | all isDigit w = Num (stringToNum (reverse w))
  1080.   | otherwise     = Word w
  1081.  
  1082. -- converts a string to a positive integer
  1083. stringToNum :: String -> Int
  1084. stringToNum (d:[]) = charToDigit d
  1085. stringToNum (d:ds) = (charToDigit d) + 10 * stringToNum ds
  1086.  
  1087. -- converts a character to a digit
  1088. charToDigit :: Char -> Int
  1089. charToDigit c = ord c - ord '0'
  1090.  
  1091.  
  1092. -- Parse command statements --
  1093.  
  1094. -- parses commands
  1095. -- format:  (<name> <arg1> <arg2> ...)
  1096. makeCommand :: CommandName -> ParseType
  1097. makeCommand n ts inS fail succ
  1098.   = parseArgs CloseParen ts inS fail $ \as ts2 inS2 ->
  1099.     succ (Command n as) ts2 inS2
  1100.  
  1101.  
  1102. -- parses a list of commands that are terminated by token "term""
  1103. parseArgs :: Token -> ParseClauseType
  1104. parseArgs term [] (i:is,ls) fail succ
  1105.   = let (ts,ls2) = lexDispatch ls i
  1106.     in parseArgs term ts (is,ls2) fail succ
  1107. parseArgs term (t:ts) inS fail succ
  1108.   | t == term = succ [] ts inS
  1109.   | otherwise = parse (t:ts) inS fail $ \a ts2 inS2 ->
  1110.                 parseArgs term ts2 inS2 fail $ \as ts3 inS3 ->
  1111.                 succ (a:as) ts3 inS3
  1112.  
  1113.  
  1114. -- Parse I/O statements --
  1115.  
  1116. -- parses read statements
  1117. -- format:  (READ)
  1118. makeRead :: ParseType
  1119. makeRead (CloseParen:ts) inS fail succ
  1120.   = succ Read ts inS
  1121. makeRead _ inS fail _
  1122.   = fail "Read:  too many arguments" inS
  1123.  
  1124. -- parses print statements
  1125. -- format:  (PRINT <arg1>)
  1126. makePrint :: ParseType
  1127. makePrint ts inS fail succ
  1128.   = parseArgs CloseParen ts inS fail $ \as ts2 inS2 ->
  1129.     if (length as) == 1
  1130.       then succ (Print as) ts2 inS2
  1131.       else fail "Print:  too many arguments" inS
  1132.  
  1133.  
  1134.  
  1135. -- Parse TO statements --
  1136.  
  1137.  
  1138. -- parses to statements
  1139. -- format:  (TO <name> <fpname1> <fpname2> ... <clause>)
  1140. -- note:  all formal parameter names must begin with a colon
  1141. makeProc :: ParseType
  1142. makeProc [] (i:is,ls) fail succ
  1143.   = let (ts,ls2) = lexDispatch ls i
  1144.     in makeProc ts (is,ls2) fail succ
  1145. makeProc ((Normal t):ts) inS fail succ
  1146.   = parseFormals ts inS fail $ \p ts2 inS2 -> 
  1147.     getParen ts2 inS2 fail $ \ts3 inS3 ->
  1148.     succ (To t p) ts3 inS3
  1149. makeProc _ inS fail _
  1150.   = fail "Invalid procedure name" inS
  1151.  
  1152. -- parses the formal parameters
  1153. -- takes all words beginning with a colon, and assumes everything
  1154. --   after that is part of the body
  1155. parseFormals :: [Token] -> InputState -> ParseFailType ->
  1156.                 (([NameType] , ClauseType) -> [Token] -> InputState -> IO ())
  1157.                 -> IO ()
  1158. parseFormals [] (i:is,ls) fail succ
  1159.   = let (ts,ls2) = lexDispatch ls i
  1160.     in parseFormals ts (is,ls2) fail succ
  1161. parseFormals (OpenBracket:ts) inS fail succ
  1162.   = parseClause (OpenBracket:ts) inS fail $ \pb ts2 inS2 ->
  1163.     succ ([],pb) ts2 inS2
  1164. parseFormals ((Normal (':':c:cs)):ts) inS fail succ
  1165.   = parseFormals ts inS fail $ \(formals,pb) ts2 inS2 ->
  1166.     succ ((':':c:cs):formals , pb) ts2 inS2
  1167. parseFormals ts inS fail succ
  1168.   = parseClause ts inS fail $ \pb ts2 inS2 ->
  1169.     succ ([],pb) ts2 inS2
  1170.  
  1171.  
  1172. -- Parse MAKE statements --
  1173.  
  1174. -- parses make statements
  1175. -- format:  (MAKE <name> <arg>)
  1176. -- note:  <name> must be quoted
  1177. makeMake :: ParseType
  1178. makeMake [] (i:is,ls) fail succ
  1179.   = let (ts,ls2) = lexDispatch ls i
  1180.     in makeMake ts (is,ls2) fail succ
  1181. makeMake ((QuotedWord s):ts) inS fail succ
  1182.   = parse ts inS fail $ \a ts2 inS2 ->
  1183.     getParen ts2 inS2 fail $ \ts3 inS3 ->
  1184.     succ (Make s a) ts3 inS3
  1185. makeMake _ inS fail _
  1186.   = fail "Make:  Improper variable name" inS
  1187.  
  1188.  
  1189. -- Parse LOCAL statements --
  1190.  
  1191. -- parses local statements
  1192. -- format:  (LOCAL <name>)
  1193. -- note:  <name> must be quoted  
  1194. makeLocal :: ParseType
  1195. makeLocal [] (i:is,ls) fail succ
  1196.   = let (ts,ls2) = lexDispatch ls i
  1197.     in makeLocal ts (is,ls2) fail succ
  1198. makeLocal (t:[]) (i:is,ls) fail succ
  1199.   = let (ts,ls2) = lexDispatch ls i
  1200.     in makeLocal (t:ts) (is,ls2) fail succ
  1201. makeLocal ((QuotedWord s):CloseParen:ts) inS fail succ
  1202.   = succ (Local s) ts inS
  1203. makeLocal _ inS fail _
  1204.   = fail "Local:  improper variable name" inS
  1205.  
  1206.  
  1207. -- Parse IF statements --
  1208.  
  1209. -- parses if-then and if-then-else statements
  1210. -- format:  (IF <cond> then <clause> [else <clause>])
  1211. makeIf :: ParseType
  1212. makeIf [] (i:is,ls) fail succ
  1213.   = let (ts,ls2) = lexDispatch ls i
  1214.     in makeIf ts (is,ls2) fail succ
  1215. makeIf ts inS fail succ
  1216.   = parse ts inS fail $ \cond ts2 inS2 ->
  1217.     parseThen ts2 inS2 fail $ \thens elses ts3 inS3 ->
  1218.     getParen ts3 inS3 fail $ \ts4 inS4 ->
  1219.     succ (If cond thens elses) ts4 inS4
  1220.  
  1221.  
  1222. -- parses then clauses
  1223. parseThen :: [Token] -> InputState -> ParseFailType ->
  1224.              (ClauseType -> ClauseType -> [Token] -> InputState -> IO ()) -> 
  1225.              IO ()
  1226. parseThen [] (i:is,ls) fail succ
  1227.   = let (ts,ls2) = lexDispatch ls i
  1228.     in parseThen ts (is,ls2) fail succ
  1229. parseThen ((Normal "THEN"):ts) inS fail succ
  1230.   = parseClause ts inS fail $ \thens ts2 inS2 ->
  1231.     parseElse ts2 inS2 fail $ \elses ts3 inS3 ->
  1232.     succ thens elses ts3 inS3
  1233. parseThen _ inS fail _
  1234.   = fail "IF:  improper THEN clause" inS
  1235.  
  1236. -- parses (optional) else clauses
  1237. parseElse :: ParseClauseType
  1238. parseElse [] (i:is,ls) fail succ
  1239.   = let (ts,ls2) = lexDispatch ls i
  1240.     in parseElse ts (is,ls2) fail succ
  1241. parseElse (CloseParen:ts) inS fail succ
  1242.   = succ [] (CloseParen:ts) inS
  1243. parseElse ((Normal "ELSE"):ts) inS fail succ
  1244.   = parseClause ts inS fail succ
  1245. parseElse _ inS fail _
  1246.   = fail "IF:  improper ELSE clause" inS
  1247.  
  1248. -- parses clauses
  1249. -- a clause is either a list of commands enclosed in brackets, or a
  1250. --   single command
  1251. parseClause :: ParseClauseType
  1252. parseClause [] (i:is,ls) fail succ
  1253.   = let (ts,ls2) = lexDispatch ls i
  1254.     in parseClause ts (is,ls2) fail succ
  1255. parseClause (OpenBracket:ts) inS fail succ
  1256.   = parseArgs CloseBracket ts inS fail succ
  1257. parseClause ts inS fail succ
  1258.   = parse ts inS fail $ \a ts2 inS2 ->
  1259.     succ [a] ts2 inS2
  1260.  
  1261.  
  1262. -- Parse Loop Statements --
  1263.  
  1264. -- parses loop statements
  1265. -- basically a dispatcher for other parse functions
  1266. makeLoop :: NameType -> ParseType
  1267. makeLoop "DO"     = makeDo
  1268. makeLoop "WHILE"  = makeWhile
  1269. makeLoop "REPEAT" = makeRepeat
  1270.  
  1271. -- parses do statements
  1272. -- format:  (DO <clause> WHILE <cond>)
  1273. makeDo :: ParseType
  1274. makeDo ts inS fail succ
  1275.   = parseClause ts inS fail $ \insts ts2 inS2 ->
  1276.     parseWhileCond ts2 inS2 fail $ \cond ts3 inS3 ->
  1277.     getParen ts3 inS3 fail $ \ts4 inS4 ->
  1278.     succ (Loop Do cond insts) ts4 inS4
  1279.  
  1280. -- parses while conditions (both in while and do-while loops)
  1281. -- a condition is simply a command that (hopefully) returns a boolean
  1282. parseWhileCond :: ParseType
  1283. parseWhileCond [] (i:is,ls) fail succ
  1284.   = let (ts,ls2) = lexDispatch ls i
  1285.     in parseWhileCond ts (is,ls2) fail succ
  1286. parseWhileCond ((Normal "WHILE"):ts) inS fail succ
  1287.   = parse ts inS fail succ
  1288.  
  1289. -- parses while statements
  1290. -- format:  (WHILE <cond> <clause>)
  1291. makeWhile :: ParseType
  1292. makeWhile ts inS fail succ
  1293.   = parse ts inS fail $ \cond ts2 inS2 ->
  1294.     parseClause ts2 inS fail $ \insts ts3 inS3 ->
  1295.     getParen ts3 inS3 fail $ \ts4 inS4 ->
  1296.     succ (Loop While cond insts) ts4 inS4
  1297.  
  1298. -- parses repeat statements
  1299. -- format:  (REPEAT <num> TIMES <clause>)
  1300. -- note:  <num> is simply a command that (hopefully) returns an integer
  1301. makeRepeat :: ParseType
  1302. makeRepeat ts inS fail succ
  1303.   = parse ts inS fail $ \num ts2 inS2 ->
  1304.     parseRepeatBody ts2 inS fail $ \insts ts3 inS3 ->
  1305.     getParen ts3 inS3 fail $ \ts4 inS4 ->
  1306.     succ (Loop Repeat num insts) ts4 inS4
  1307.  
  1308. -- parses repeat body (just a clause)
  1309. parseRepeatBody :: ParseClauseType
  1310. parseRepeatBody [] (i:is,ls) fail succ
  1311.   = let (ts,ls2) = lexDispatch ls i
  1312.     in parseRepeatBody ts (is,ls2) fail succ
  1313. parseRepeatBody ((Normal "TIMES"):ts) inS fail succ
  1314.   = parseClause ts inS fail succ
  1315. parseRepeatBody _ inS fail _
  1316.   = fail "Repeat:  invalid format" inS
  1317.  
  1318.  
  1319. -- Parse Graphics Statements --
  1320.  
  1321. -- parses all side-effecting graphics statements
  1322. makeGraphics :: CommandName -> ParseType
  1323. makeGraphics n ts inS fail succ
  1324.   = parseArgs CloseParen ts inS fail $ \as ts2 inS2 ->
  1325.     succ (Graphics n as) ts2 inS2
  1326.  
  1327. -- Parse Trailing Parenthesis --
  1328.  
  1329. -- parses the closing paren terminating most commands
  1330. getParen :: [Token] -> InputState -> ParseFailType ->
  1331.             ([Token] -> InputState -> IO ()) -> IO ()
  1332. getParen [] (i:is,ls) fail succ
  1333.   = let (ts,ls2) = lexDispatch ls i
  1334.     in getParen ts (is,ls) fail succ
  1335. getParen (CloseParen:ts) inS fail succ
  1336.   = succ ts inS
  1337. getParen _ inS fail _
  1338.   = fail "Expected )" inS
  1339.  
  1340.